<?php

1->一对一之间的配置问题
   -) '前景回顾'
       如果只有'config-clien <=> config-server'这哥俩搞来搞去, 也顶多算是自己内部流转, 算不得什么,
       一旦高并发情况下就很有可能'玩完...>_<'

   -) '核心'
       竟然2个人玩没意思, 那就考虑加入'大家庭-eureka', 将'config-server -> eureka'上面形成集群模式,
       这样的话, 就作为集群对外提供配置中心, 稳稳的幸福. ^ - ^


2->高可用配置中心
   -) '[config-server + config-client] - 集成eureka'
       <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
       </dependency>

   -) '[config-server + config-client] - 注册eureka'
       eureka:
        client:
         service-url:
           defaultZone: http://localhost:9500/eureka/

   -) 'config-client - 配置文件'
       修改: 将原来的'直连'config-server 的uri (uri: http://localhost:9001), 修改为[查询eureka的server-id]
       spring:
         application:
           name: config-client
         cloud:
           config:
      #       uri: http://localhost:9001    # 单节点连接 - 原始
             profile: prod                  # 指定读取文件的【环境】，一般是：外部指定启动环境
             label: master
             name: config-consumer          # 与Gitee上面的服务名保持一致
             enabled: true                  # 是否开启 [配置服务] (可定义变量)
               discovery:
                 enabled: true              # 是否开启 [配置服务发现] (可定义变量)
                 service-id: config-server  # 指定[config-server]在eureka上的id


3->配置节点过多引发的问题与展望
   -) '思考'
       我们知道, 随着微服务的数量越来越多, 集成配置中心的服务也越来越多! 那么一旦'新增 / 修改'配置文件内容的话,
       我们要一台一台'手动'去'refresh'吗?!?!?!
       那么有没有一个功能或者组件, 可以实现只触发一次请求, 就能同时号令'千万节点'共同更新配置?

   -) '解决方案 - 消息总线'
#      农会会员漫山遍野，梭镖短棍一呼百应，土匪无处藏踪.
#                                             汉·韩婴《韩诗外传》
       Any problem in computer science can be solved by anther layer of indirection.
       - 计算机科学领域的任何问题都可以通过增加一个间接中间层来解决.
       从这个角度出发，让我们对这个中间层提两点需求:
       . 响应事件: 它可以对变更事件做出响应，并且将这个事件路由到一个或多个服务节点.
       . 消费事件: 各个服务节点都可以从这个组件中消费事件.

       上面的需求无非就是设立一个中间层作为消息代理，向服务节点下发通知。我们想一下目前最常用的消息分发策略，这个需求是不是和“订阅-发布”模型非常类似？
       由各个节点订阅变更事件，当变更事件发生以后，从中间层获取消息并消费这个变更请求.


4->配置管理之————安全
   -) '说明'
       通常来说存储到配置文件里的大多都是一些并不敏感的业务数据，所以我们就光明正大地把明文放到GitHub中去了。如果要保存敏感的数据 (生产环境数据库的用户名和密码),
       并且需要通过配置管理的方式分发到服务节点，但又不能直接把密码放到GitHub，这时候应该怎么保护这些隐私数据的安全性呢？

       前面提到的数据库密码是一个相当机密的信息，尽量不要让安全级别不高的个人或团队直接接触到。尽管我们程序猿们个个都是信得过的，
       不会随便登录生产环境做破坏性操作，可也不能保证突发事件的发生。比如今天我和产品经理干了一架，打输了，气不过，拿着密码登录生产环境删库跑路了...

   -) '数据加密'
       我们需要引入一个数据安全机制，减少敏感数据的暴露程度，因此需要对三个环节做严格控制:
       A. 数据加密端:
       就像颁发CA证书一样，必须由一个中心机构用密钥对明文进行加密。这里也一样，需要在生产环境中创建一个特定的服务，专门做加密操作。
       最重要的是, 加密所用的秘钥只能保存在那台机器里, 不能对外暴露!

       B. 数据存放端: 数据存放端(如GitHub), 只能保存'加密过后'的数据.

       C. 数据解密端:
       由于密钥都保存在数据加密端, 因此数据解密和数据加密'必须放在同一个地方进行', 加密数据在'传给客户端之前'要经过解密,
       不能将秘钥下放到客户端机器上进行解密!

       A1:
       数据加密, 在进行加密之前，需要在Config的启动项中添加密钥信息.
       Config支持'对称加密'和'非对称加密'的方式, 经由Config'将明文加密'为一串密文, 然后我们将加密后的字符串提交到GitHub上.
       加密这一步可以通过['HTTP POST']方式将未加密的明文提交到Config服务下的'/encrypt'路径 (如：localhost:60001/encrypt，明文放在BODY中)
       调用成功后Config会返回加密字符串.
       在保存到Github的时候，需要在加密数据前面添加 '{cipher}' 标识, 告诉Config这是一段加密存储的信息，
       例如: password: '{cipher}69f734a8f0994f77a104c0bd4856d3e8e753b52'

       C1:
       服务端解密, 当服务发起请求从Config拉取数据的时候，如果远程属性包含标识头'{cipher}',
       将被Config视为加密信息(这就是一种约定大于配置的设计思想), Config将使用'密钥'对属性进行解密后再返回明文给客户端!


5->加密的种类
   -) '对称加密'
       对称加密算法要求发送者和接收者在进行信息交换之前，商定一个密钥，即加密和解密两步都使用同一个密钥来进行。如果大家上学的时候选修过密码学就会知道，
       对称加密算法运算速度快，比较高效。不过有个潜在的风险存在，其密钥只能分发给绝对信任的Client（授信服务），否则任何获得密钥的人都可以使用该密钥进行通信。
       + 运算速度快, 高效
       - 安全性低

   -) '非对称加密'
       非对称加密算法需要生成一个密钥对，包括公钥和私钥，它们是'成对出现'的，就像一把锁和一把钥匙，只有特定的钥匙才能开启对应的锁。
       公钥: 用来对信息进行加密,
       私钥: 进行解密。
       非对称算法强度复杂, 加解密速度没有对称秘钥快，但是它解决了秘钥分发的安全性问题。
       + 安全性高
       - 强度复杂, 运算速度慢

       有一个交易对账功能，服务A将交易数据发给服务B，服务B再返回处理结果到A。首先他们要协商出两对公钥和私钥（A和B各有一对不同的密钥对），
       A和B这时互相交换公钥，当A给B发送数据的时候，使用B提供的公钥进行加密，B使用自己保留的私钥对A发来的数据进行解密。反过来也一样，
       B使用A提供的公钥对处理结果进行加密之后再发给A，A收到B发来的数据后使用自己的私钥来解密.

   -) '公钥是公开的吗'
       很多同学看到“公钥”会以为这个是公开的，这个说法只说对了一半。首先公钥也是密钥的一部分，它所谓的“公开”是针对授信访问者来说的，也就是说，
       在授信访问者的圈子里，它是公开的。假如你的服务可以被任何人调用，那么这个授信访问者就是everybody，这个公钥便成了真正的无限制公开；
       假如你的应用只能被指定的几个服务调用，那就只能将这些公钥分发给指定服务，对你而言除了这几个授信服务以外的应用都不应该拿到这个公钥。

       一个真实的案例来说明这个问题: 在18年的时候，腾讯向某地公安机构报案，声称其服务器被非法入侵。经过一系列的侦查，
       发现有一家颇具规模的技术公司通过反编译的手段反编了微信的安卓安装包，之后又从安装包内找到了信息传输的公钥和微信通讯协议的规则。
       这家公司将这个公钥集成到了自己的app里，用相同的通讯协议向腾讯服务器发起各种服务请求，将自己的app打造成了微信的“代理”。
       由于在通信过程中使用的是真实的公钥，腾讯服务器也把它当做了授信访问者。殊不知，这个“公钥”只是腾讯发给微信这个授信app的，
       他人盗取公钥的行为实际上已经触犯了法律，而这家公司的相关负责人也被检查机关以“非法入侵计算机系统罪”提起公诉。


6->使用————对称加密
   -) '下载JCE组件'
       替换java下的'jce'进行不限长加密 (1.8.6之后无需下载)
       https://www.oracle.com/java/technologies/javase-jce-all-downloads.html
       然后替换: java_home/jre/lib/security

   -) 'config-server - bootstrap.yml'
       encrypt:
         key: 20210330

       注意: 此处必须配置在'bootstrap.yml'里面, 要不然就报:
       {
          "description": "The encryption algorithm is not strong enough",
          "status": "INVALID"
       }

   -) '使用'
       . 查看是否开启 (GET): /encrypt/status

       . 加密 (POST - Body.text): /encrypt/
         示例: root123456
         e015a7c504626d40fd6fe34a3930098829c09be7869addd5f654a59878103452

       . 解密 (POST - Body.text): /decrypt/
         示例: e015a7c504626d40fd6fe34a3930098829c09be7869addd5f654a59878103452
         root123456

   -) '配置文件'
       将Postman生成的'加密后'的数据, 上传到Gieet上.
       password: '{cipher}4b6d0db4071843b038e9e1f442c7195f67661496e3449c7fc93d8f2da77f9a51'
       注意:
       . 放在config-server的'bootstrap.yml'文件中配置!
       . 一定要用'{cipher}加密后的数据', 否则config-server拉取配置会报错!
       . 加解密的密钥要一致! 如果不一致的话, 也会报错!
       . 如果[config-server]的'密钥更新'而没有'重新上传'的话, [config-client]refresh后, 会报错:
         Could not resolve placeholder 'mysql.password' in value "${mysql.password}"] with root cause
         解决: 将[config-server]生成的密钥重新上传 ---> 然后[config-client.refresh]就行了 ^-^

   -) '交互'
       +---------------+            +---------------+ -------key(加密)-------> +---------------+
       | config-client | ---------> | config-server |                         |     Gieet     |
       +---------------+            +---------------+ <------key(解密)-------- +---------------+


7->常用套路
   -) '环境隔离'
       针对项目对应的不同环境可以配置:
                                +---------------+
                                | config-server |
                                +---------------+
                                        |
                        +---------------+---------------+
                        |               |               |
                     日常环境          预发环境         生产环境
                (timeout=10000ms)(timeout=5000ms)(timeout=1000ms)
                        +---------------+---------------+
                                        |
                                     Git文件名
                                       Path
                                      Branch

   -) '业务开关 + 定向推送'
       利用config-clien需要'actuator/refresh'更新配置文件, 可以做到:
                               +---------------+
                               | config-server |
                               +---------------+
                                       |
             +------------+------------+------------+------------+
             |            |           开启         开启           |
          Server1      Server2     'Server3'    'Server4'     Server5

       ABTest: 依赖'定向推送'可以做: 灰度发布, 蓝绿发布, 金丝雀测试.

   -) '修改业务逻辑'
       我们也可以利用'动态刷新'结合业务逻辑实现一些场景:
                                    +---------------+
                                    | config-server |
                                    +---------------+
                                            |
                      +---------------------+---------------------+
                      |                     |                     |
                   网关黑名单            费率-规则引擎             熔断阈值
                 www.baidu.com              |                     |
                      |                     +----------|----------+
                    网关层                           下单接口


8->扩展
   -) '动态开关'
       在配置仓库中配置一个boolean类型的开关, 然后在项目代码里引用 (注意: 引用的类要加上注解), 这样就实现'动态开关'!
       @Configuration
       @RefreshScope
       @Data
       public class ConfigProperties {

          # 引用 [远程仓库] 配置的属性!
          @Value("${registration.disable}")
          private Boolean disableRegistration;

       }

   -) '金丝雀测试'
       金丝雀测试是一种轻量级的灰度发布策略，在做版本发布的时候，先将新版本部署到一台服务器上，这台服务器我们就称之为“金丝雀”，
       区别于其他部署了老版本代码的服务器。金丝雀开始运作以后，经过一定时间的孵化期，其各项指标（如HTTP请求错误率，响应时间，CPU使用率等）都在正常情况下，
       这时候可以逐渐把新版本部署到其他服务器。

       在实际应用中，我们可以通过配置中心的'功能开关'来代替部署发布流程，先将代码发布上线，功能处于关闭状态，
       再通过配置中心将开启功能的配置推送到一台服务器上，以此来完成金丝雀测试。
